{
 "nbformat": 4,
 "nbformat_minor": 0,
 "metadata": {
  "colab": {
   "private_outputs": true,
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/mikexcohen/Statistics_book/blob/main/stats_ch03_visualization.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Modern statistics: Intuition, Math, Python, R\n",
    "## Mike X Cohen (sincxpress.com)\n",
    "#### https://www.amazon.com/dp/B0CQRGWGLY\n",
    "#### Code for chapter 3 (visualization)\n",
    "\n",
    "---\n",
    "\n",
    "# About this code file:\n",
    "\n",
    "### This notebook will reproduce most of the figures in this chapter (some figures were made in Inkscape), and illustrate the statistical concepts explained in the text. The point of providing the code is not just for you to recreate the figures, but for you to modify, adapt, explore, and experiment with the code.\n",
    "\n",
    "### Solutions to all exercises are at the bottom of the notebook.\n",
    "\n",
    "#### This code was written in google-colab. The notebook may require some modifications if you use a different IDE."
   ],
   "metadata": {
    "id": "yeVh6hm2ezCO"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "oYWhHwGQ9OdD"
   },
   "outputs": [],
   "source": [
    "# import libraries and define global settings\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.patches import Polygon # to create patches\n",
    "\n",
    "# define global figure properties used for publication\n",
    "import matplotlib_inline.backend_inline\n",
    "matplotlib_inline.backend_inline.set_matplotlib_formats('svg') # display figures in vector format\n",
    "plt.rcParams.update({'font.size':14,             # font size\n",
    "                     'savefig.dpi':300,          # output resolution\n",
    "                     'axes.titlelocation':'left',# title location\n",
    "                     'axes.spines.right':False,  # remove axis bounding box\n",
    "                     'axes.spines.top':False,    # remove axis bounding box\n",
    "                     })"
   ]
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "Ls2d_er6jq2u"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.2: The math of the heart :)"
   ],
   "metadata": {
    "id": "qDfibMxpjqzm"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "import sympy as sym\n",
    "\n",
    "x,y = sym.symbols('x,y')\n",
    "expr = (x**2 + y**2 - 1)**3 - x**2 * y**3\n",
    "\n",
    "# just need the output, not the plot\n",
    "h = sym.plot_implicit(expr,show=False)\n",
    "\n",
    "# grab midpoints of intervals and plot those\n",
    "data = np.array([(x_int.mid, y_int.mid) for x_int, y_int in h[0].get_points()[0]])\n",
    "plt.plot(data[:,0],data[:,1],'k.',markersize=2)\n",
    "plt.yticks([-1,0,1])\n",
    "plt.axis('square')\n",
    "\n",
    "plt.savefig('vis_heart.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "5mhmQCFOkC40"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "6g3ywzJulnMt"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.3: Bar plot of news sources"
   ],
   "metadata": {
    "id": "5tNO0r82jwUe"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# specify the bar heights and labels\n",
    "news_sources = [40,10,85,20]\n",
    "source_labels = ['TV','Newspapers','Internet','Word of mouth']\n",
    "\n",
    "# show the bar plot\n",
    "plt.figure(figsize=(8,3.3))\n",
    "plt.bar(source_labels,news_sources,color=[.2,.2,.2])\n",
    "\n",
    "# make the graph look a bit nicer\n",
    "plt.title('Where do people get their news?',loc='center')\n",
    "plt.ylabel('Percent responding \"yes\"')\n",
    "plt.xlabel('Media type')\n",
    "plt.xticks(rotation=-30)\n",
    "\n",
    "plt.savefig('vis_barplot_news1.png',bbox_inches='tight')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "9bSa_913iED6"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "woWJSeM6DuH7"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.4: Margin figure of bar plot"
   ],
   "metadata": {
    "id": "y7ZxkTBXDtbg"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "Y = [1,4,3,9] # bar heights\n",
    "X = [0,1,3,4] # bar locations\n",
    "\n",
    "plt.figure(figsize=(5,2.5))\n",
    "plt.bar(X,Y,color='k')\n",
    "plt.xlabel('X')\n",
    "plt.ylabel('Y')\n",
    "plt.savefig('vis_barplot_basic.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "AtqZvoLODvtB"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "sf3xFxxODtYv"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.5: Grouped bar plots"
   ],
   "metadata": {
    "id": "cPLfIz3rf1zk"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "news_sources = np.array([[12,17,95,35],\n",
    "                         [90,40,50,25]] )\n",
    "agegroups = ['Millennials','Boomers']\n",
    "\n",
    "_,axs = plt.subplots(1,2,figsize=(10,5))\n",
    "\n",
    "axs[0].bar(np.arange(4)-.2,news_sources[0],width=.4,color='k')\n",
    "axs[0].bar(np.arange(4)+.2,news_sources[1],width=.4,color='gray')\n",
    "axs[0].set(xlabel='Media type',ylabel='Percentage responding \"yes\"',xticks=np.arange(4))\n",
    "axs[0].set_xticklabels(source_labels,rotation=-30)\n",
    "axs[0].legend(agegroups)\n",
    "axs[0].set_title(r'$\\bf{A}$)  Grouped by news source')\n",
    "\n",
    "\n",
    "offset = [-.3,-.1,.1,.3]\n",
    "hatches = 'xo-|' # \"hatch\" is the fill shape\n",
    "for i in range(4):\n",
    "  axs[1].bar(np.arange(2)+offset[i],news_sources[:,i],width=.2,\n",
    "             hatch=hatches[i],color=[.8,.8,.8],edgecolor='k')\n",
    "\n",
    "# Note about color: Unless you need grayscale, color is nicer-looking than hatches:\n",
    "# for i in range(4): axs[1].bar(np.arange(2)+offset[i],news_sources[:,i],width=.2)\n",
    "\n",
    "axs[1].set(xlabel='Generation',ylabel='Percentage responding \"yes\"',xticks=np.arange(2))\n",
    "axs[1].set_xticklabels(agegroups)\n",
    "axs[1].legend(source_labels,fontsize=10)\n",
    "axs[1].set_title(r'$\\bf{B}$)  Grouped by generation')\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_barplot_news2.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "iy5YW0Upu6Qh"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "sjG_MZii-gOj"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.6: Note about bars from matrices"
   ],
   "metadata": {
    "id": "8UX9w-RN-gLo"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# data are observations (rows) X features (columns)\n",
    "m = [ [2,5,4,3], [1,1,8,6] ]\n",
    "m = [ [10,12,90,35], [85,15,50,10] ]\n",
    "\n",
    "fig,ax = plt.subplots(nrows=2,ncols=2,figsize=(8,8))\n",
    "\n",
    "\n",
    "### show data as an image\n",
    "# conceptualizing the data as <row> groups of <columns>\n",
    "ax[0,0].imshow(m,cmap='gray',vmin=-30,vmax=90)\n",
    "\n",
    "# add numerical labels\n",
    "for i in range(len(m)):\n",
    "  for j in range(len(m[0])):\n",
    "    ax[0,0].text(j,i,m[i][j],fontsize=30\n",
    "                 ,horizontalalignment='center',verticalalignment='center')\n",
    "ax[0,0].set(xticks=[],yticks=[])\n",
    "ax[0,0].set(xlabel='Features',ylabel='Observations')\n",
    "\n",
    "\n",
    "# now other orientation (features X observations)\n",
    "ax[0,1].imshow(np.array(m).T,cmap='gray',vmin=-30,vmax=90)\n",
    "for i in range(len(m)):\n",
    "  for j in range(len(m[0])):\n",
    "    ax[0,1].text(i,j,m[i][j],fontsize=30\n",
    "                 ,horizontalalignment='center',verticalalignment='center')\n",
    "ax[0,1].set(xticks=[],yticks=[])\n",
    "ax[0,1].set_xlabel('Observations')\n",
    "ax[0,1].set_ylabel('Features')\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "#### now for bar plots using pandas dataframe\n",
    "df = pd.DataFrame(m,columns=['A','B','C','D'])\n",
    "h = df.plot(ax=ax[1,0],kind='bar',rot=0)\n",
    "ax[1,0].set_ylim([0,100])\n",
    "\n",
    "# add text labels\n",
    "for p in h.patches:\n",
    "  h.annotate(p.get_height(), [p.get_x()+.06, p.get_height()+1],\n",
    "             horizontalalignment='center',fontsize=12)\n",
    "\n",
    "## add text/number indices to rows/columns\n",
    "for i,l in zip(range(4),'ABCD'):\n",
    "  ax[0,0].text(i,-.6,l,ha='center',fontsize=16)\n",
    "  ax[0,1].text(1.65,i,l,ha='center',fontsize=16)\n",
    "\n",
    "for i in range(2):\n",
    "  ax[0,0].text(3.65,i,str(i),va='center',ha='center',fontsize=16)\n",
    "  ax[0,1].text(i,-.6,str(i),ha='center',fontsize=16)\n",
    "\n",
    "\n",
    "ax[1,0].set_xlabel('Index')\n",
    "\n",
    "h = df.T.plot(ax=ax[1,1],kind='bar',rot=0)\n",
    "ax[1,1].set_ylim([0,100])\n",
    "for p in h.patches:\n",
    "  h.annotate(p.get_height(), [p.get_x()+.12, p.get_height()+1],\n",
    "             horizontalalignment='center',fontsize=12)\n",
    "\n",
    "ax[1,1].set_xlabel('Index')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_barplotOrientations.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "GajAmItO-gJI"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "gnzUQLkl-gA2"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.8: Pie chart"
   ],
   "metadata": {
    "id": "jBnKZfYvppiq"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# data\n",
    "mostNews = [ 15,5,70,10 ]\n",
    "\n",
    "# make the pie chart\n",
    "plt.figure(figsize=(6.3,6.3))\n",
    "plt.pie(mostNews,labels=source_labels,autopct='%.0f%%',\n",
    "        radius=1.2,wedgeprops={'edgecolor':'k'})\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_pie.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "hIYV2M7ADHhJ"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "8FkndMENDHjp"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.9: Box plot"
   ],
   "metadata": {
    "id": "wTqH9XBupuYH"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# random data with an outlier\n",
    "data = np.random.randn(100)\n",
    "data[data>2] = 1\n",
    "data[data<-2] = -1\n",
    "data[-1] = 3 # force one outlier\n",
    "\n",
    "\n",
    "\n",
    "plt.figure(figsize=(2,4))\n",
    "\n",
    "# draw the boxplot and make some color adjustments\n",
    "h = plt.boxplot(data,patch_artist=True)\n",
    "h['boxes'][0].set(color='k')\n",
    "h['medians'][0].set(color='w')\n",
    "\n",
    "plt.xlim([.8,1.5])\n",
    "plt.xticks([])\n",
    "plt.ylabel('Data values')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_boxplotBasic.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "CYCsZayGDHmB"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "CchkDu2-gTia"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.10: Box plots for distribution characteristics"
   ],
   "metadata": {
    "id": "y2nGgrO9gT1Z"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# box plot\n",
    "data = np.hstack( (np.random.normal(loc=100,size=(200,1),scale=10),\n",
    "                   np.random.normal(loc=100,size=(200,1),scale=2) ))\n",
    "\n",
    "# draw the boxplot and make some color adjustments\n",
    "plt.figure(figsize=(3,3))\n",
    "h = plt.boxplot(data,patch_artist=True,widths=.7)\n",
    "for (b,m) in zip(h['boxes'],h['medians']):\n",
    "  b.set(color='k')\n",
    "  m.set(color='w')\n",
    "\n",
    "plt.xlim([.5,2.5])\n",
    "plt.xticks(range(1,3),['A','B'])\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_boxplotComp.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "nf6DMDG3DHom"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "n9DdHqWZ-f-A"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.11: Histogram"
   ],
   "metadata": {
    "id": "SmCib5Q_qNMh"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# version 1\n",
    "\n",
    "X = [ 1,2,2,3,3,4,5,5,5,5,6,7,7,7,8,8,9 ]\n",
    "\n",
    "plt.figure(figsize=(8,4))\n",
    "plt.hist(X,bins=len(set(X)),color='gray',edgecolor='k')\n",
    "plt.xticks(np.arange(np.min(X),np.max(X)+1))\n",
    "plt.xlabel('Numerical value')\n",
    "plt.ylabel('Count')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_histOfInts1.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "WP9NrLNTgNiv"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.12"
   ],
   "metadata": {
    "id": "B1abWiYigef8"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# using different boundaries\n",
    "plt.figure(figsize=(8,4))\n",
    "plt.hist(X,bins=np.arange(.5,9.51,step=1),color='gray',edgecolor='k')\n",
    "plt.xticks(np.arange(np.min(X),np.max(X)+1))\n",
    "plt.xlabel('Numerical value')\n",
    "plt.ylabel('Count')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_histOfInts2.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "l30Xh0dUgNlc"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# the table of bin boundaries\n",
    "_,x1 = np.histogram(X,bins=len(set(X)))\n",
    "_,x2 = np.histogram(X,np.arange(.5,9.51,step=1))\n",
    "\n",
    "for i in range(len(x1)-1):\n",
    "  print(f'Bin {i+1}:  [{x1[i]:.1f} , {x1[i+1]:.1f}]   [{x2[i]:.1f} , {x2[i+1]:.1f}]')"
   ],
   "metadata": {
    "id": "aMFSgclngNoB"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "6ZXTJMy5gNqf"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.14: Histogram of mongoose lengths"
   ],
   "metadata": {
    "id": "Vpq9XiQdq__z"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# distribution of mongooses' lengths\n",
    "mongooses = np.arctanh(np.random.uniform(-.75,.75,size=500))*15+40\n",
    "\n",
    "plt.figure(figsize=(8,4))\n",
    "plt.hist(mongooses,bins=30,color='gray',edgecolor='k')\n",
    "\n",
    "plt.xlabel('Length (cm)')\n",
    "plt.ylabel('Count')\n",
    "plt.title('Lengths of Babylonian mongooses',loc='center')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_mongeese.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "jXMmJl_MsZkX"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "s0VFFleLgoND"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.15: Histograms with different bins"
   ],
   "metadata": {
    "id": "3dNW9XCMglm5"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "_,axs = plt.subplots(1,2,figsize=(8,3))\n",
    "\n",
    "axs[0].hist(mongooses,bins=3,color='gray',edgecolor='k')\n",
    "axs[0].set_title(r'$\\bf{A}$)  Histogram with 3 bins')\n",
    "\n",
    "axs[1].hist(mongooses,bins=300,color='gray',edgecolor='k')\n",
    "axs[1].set_title(r'$\\bf{B}$)  With 300 bins')\n",
    "\n",
    "# stylizing\n",
    "for a in axs:\n",
    "  a.set_xlabel('Length (cm)')\n",
    "  a.set_ylabel('Count')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_mongeese_binoptions.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "duXc_L9zsZmb"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "pPIGyrCoEGiS"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.16: Distribution showing tails"
   ],
   "metadata": {
    "id": "wbUyhGRK2Trb"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "import scipy.stats as stats\n",
    "# create a Gaussian probability curve\n",
    "x = np.linspace(-4,4,401)\n",
    "gpdf = stats.norm.pdf(x)\n",
    "\n",
    "# the find the indices of the 2.5% and 97.5%\n",
    "lbndi = np.argmin(np.abs(x-stats.norm.ppf(.05)))\n",
    "ubndi = np.argmin(np.abs(x-stats.norm.ppf(1-.05)))\n",
    "\n",
    "\n",
    "# plot the probability function and the vertical lines\n",
    "_,ax = plt.subplots(1,figsize=(5,4))\n",
    "ax.plot(x,gpdf,'k',linewidth=2)\n",
    "ax.set(xlim=x[[0,-1]],ylim=[0,.42],xticks=[],yticks=[],\n",
    "       xlabel='Data value',ylabel='Proportion')\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# now create patches for the rejected area\n",
    "# Note: fill_between() is usually more convenient (and I use it later); here I show how to add a polygon patch FYI.\n",
    "from matplotlib.patches import Polygon\n",
    "\n",
    "dots = np.zeros((lbndi+2,2))\n",
    "for i in range(lbndi+1):\n",
    "  dots[i,:] = x[i],gpdf[i]\n",
    "dots[-1,:] = x[lbndi],0\n",
    "ax.add_patch(Polygon(dots,facecolor='k',alpha=.4))\n",
    "\n",
    "# repeat for the right lobe\n",
    "dots = np.zeros((len(x)-ubndi+1,2))\n",
    "for i in range(ubndi,len(x)):\n",
    "  dots[i-ubndi,:] = x[i],gpdf[i]\n",
    "dots[-1,:] = x[ubndi],0\n",
    "ax.add_patch(Polygon(dots,facecolor='k',alpha=.4))\n",
    "\n",
    "# annotations\n",
    "tailx = np.argmin(np.abs(x--2.2))\n",
    "ax.annotate('Left tail',xy=(x[tailx],gpdf[tailx]+.01),\n",
    "            xytext=(x[tailx]-1.1,gpdf[tailx]+.08),ha='center',\n",
    "            arrowprops={'color':'k'},weight='bold',size=16)\n",
    "tailx = np.argmin(np.abs(x-2.2))\n",
    "ax.annotate('Right tail',xy=(x[tailx],gpdf[tailx]+.01),\n",
    "            xytext=(x[tailx]+1.1,gpdf[tailx]+.08),ha='center',\n",
    "            arrowprops={'color':'k'},weight='bold',size=16)\n",
    "\n",
    "ax.axis('off')\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_distribution_tails.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "WGKpxA3DEG10"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "KSgGfpVBsZo9"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.17: Histogram raw counts vs proportion"
   ],
   "metadata": {
    "id": "p7KJ5a1nUOUr"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "data = np.random.rand(200)**2\n",
    "\n",
    "# extract histogram data\n",
    "counts,x = np.histogram(data,bins='fd')\n",
    "binCents = (x[:-1]+x[1:])/2\n",
    "\n",
    "# convert counts to proportion\n",
    "proportion = counts / np.sum(counts)\n",
    "\n",
    "_,axs = plt.subplots(1,2,figsize=(8,4))\n",
    "\n",
    "axs[0].bar(binCents,counts,width=.12,color='gray',edgecolor='k')\n",
    "axs[0].set_title(r'$\\bf{A}$)  Raw counts')\n",
    "axs[0].set_ylabel('Count')\n",
    "\n",
    "axs[1].bar(binCents,proportion,width=.12,color='gray',edgecolor='k')\n",
    "axs[1].set_title(r'$\\bf{B}$)  Proportion')\n",
    "axs[1].set_ylabel('Proportion')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_histCountVsProp.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "Jkl073XIUOXo"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "1HR6rHOpUOZ3"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.19: The mongooses experiment"
   ],
   "metadata": {
    "id": "0Ps2wS7psZrW"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "mongooses_africa = np.arctanh(np.random.uniform(size=100)*1.5-.75)*12+37\n",
    "mongooses_asia = np.arctanh(np.random.uniform(size=500)*1.5-.75)*15+42\n",
    "\n",
    "# create common bin boundaries across both datasets\n",
    "alldata = np.concatenate((mongooses_africa,mongooses_asia))\n",
    "binbounds = np.linspace(np.min(alldata),np.max(alldata),30)\n",
    "\n",
    "_,axs = plt.subplots(2,2,figsize=(8,6))\n",
    "\n",
    "# top two panels show raw histograms\n",
    "axs[0,0].hist(mongooses_africa,bins=binbounds,color='gray',edgecolor='k')\n",
    "axs[0,0].set_xlim([binbounds[0]-1,binbounds[-1]+1])\n",
    "axs[0,0].set_ylim([0,30]) # ylim hard-coded based on N and bins\n",
    "axs[0,0].set_title(r'$\\bf{A}$)  Counts: African mons')\n",
    "\n",
    "axs[0,1].hist(mongooses_asia,bins=binbounds,color='gray',edgecolor='k')\n",
    "axs[0,1].set_xlim([binbounds[0]-1,binbounds[-1]+1])\n",
    "axs[0,1].set_ylim([0,30])\n",
    "axs[0,1].set_title(r'$\\bf{B}$)  Counts: Asian mons')\n",
    "\n",
    "# bottom row for proportion\n",
    "axs[1,0].hist(mongooses_africa,bins=binbounds,density=True,\n",
    "              color='gray',edgecolor='k')\n",
    "axs[1,0].set_xlim([binbounds[0]-1,binbounds[-1]+1])\n",
    "axs[1,0].set_ylim([0,.1])\n",
    "axs[1,0].set_title(r'$\\bf{C}$)  Proportion: African mons')\n",
    "\n",
    "axs[1,1].hist(mongooses_asia,bins=binbounds,density=True,\n",
    "              color='gray',edgecolor='k')\n",
    "axs[1,1].set_xlim([binbounds[0]-1,binbounds[-1]+1])\n",
    "axs[1,1].set_ylim([0,.1])\n",
    "axs[1,1].set_title(r'$\\bf{D}$)  Proportion: Asian mons')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_mongeese_rawProp.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "q1f7E3g-8pjn"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "JFcx0aVJ989M"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.21: Violin plot from histogram"
   ],
   "metadata": {
    "id": "XGk3GO9A8pnE"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# the data\n",
    "x1 = np.random.randn(100)-.5\n",
    "x2 = np.random.randn(100)+.5\n",
    "X = np.concatenate((x1,x2))\n",
    "\n",
    "_,axs = plt.subplots(1,4,figsize=(14,4))\n",
    "\n",
    "# regular histogram\n",
    "axs[0].hist(X,bins='fd',color='gray',edgecolor='k')\n",
    "axs[0].set_title(r'$\\bf{A}$)  Step 1: histogram')\n",
    "axs[0].set_ylim([0,40])\n",
    "axs[0].set_xlabel('Data value')\n",
    "axs[0].set_ylabel('Count')\n",
    "\n",
    "# smooth interpolation of histogram\n",
    "y,x = np.histogram(X,bins='fd')\n",
    "x = (x[:-1]+x[1:])/2\n",
    "\n",
    "from scipy import interpolate\n",
    "interpF = interpolate.interp1d(x,y,kind='cubic')\n",
    "xx = np.linspace(x[0],x[-1],100)\n",
    "yy = interpF(xx)\n",
    "\n",
    "\n",
    "axs[1].plot(xx,yy,'k')\n",
    "axs[1].set_xlabel('Data value')\n",
    "axs[1].set_ylabel('Count')\n",
    "axs[1].set_ylim([0,40])\n",
    "axs[1].set_title(r'$\\bf{B}$)  Step 2: interpolate')\n",
    "\n",
    "\n",
    "# now for the violin plot (as a dataframe to use seaborn)\n",
    "df = pd.DataFrame(X)\n",
    "sns.violinplot(data=df,palette='gray',ax=axs[2])\n",
    "sns.stripplot(data=df,ax=axs[2],palette='dark:#b2b2b2')\n",
    "axs[2].set_xlabel('Count (norm.)')\n",
    "axs[2].set_ylabel('Data value')\n",
    "axs[2].set_title(r'$\\bf{C}$)  Step 3: rotate/mirror')\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# NOTE: The code below is actually a solution to Exercise 7, so don't inspect this code\n",
    "#       too carefully if you want the challenge of solving it yourself :)\n",
    "df = pd.DataFrame(np.vstack((x1,x2)).T,columns = ['x1','x2'])\n",
    "df_all = pd.DataFrame( pd.concat((df['x1'],df['x2']),axis=0),columns=['y'])\n",
    "df_all['distr'] = 'x2'\n",
    "df_all['distr'][:len(df)] = 'x1' # note: you can safely ignore the warning that this line produces\n",
    "df_all[''] = ' '\n",
    "sns.violinplot(data=df_all,x='',y='y',palette='gray',ax=axs[3],split=True,hue='distr')\n",
    "axs[3].legend_.remove()\n",
    "axs[3].set(xlim=[-.75,.75],ylabel='Data value')\n",
    "axs[3].set_title(r'$\\bf{D}$)  Asymmetric violin')\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_makeAviolin.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "umv40iAZ8pqA"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "MuNOojnDsZwA"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.22: Linear vs logarithmic plot"
   ],
   "metadata": {
    "id": "MWyOslJ6gNtI"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# simple data... just a line!\n",
    "y = np.linspace(1,10**4)\n",
    "\n",
    "# create a figure\n",
    "_,axs = plt.subplots(1,3,figsize=(12,3.5))\n",
    "\n",
    "# visualization\n",
    "for i in range(3):\n",
    "\n",
    "  # plot the line (same data in all plots!)\n",
    "  axs[i].plot(y,'k',linewidth=2)\n",
    "\n",
    "  # adjust the axes for the log plots\n",
    "  if i>0:\n",
    "    axs[i].set_yscale('log')\n",
    "    t = r'$\\bf{' + ['B','C'][i-1] + '}$)  Log scaling'\n",
    "    axs[i].set_title(t)\n",
    "    axs[i].set_ylabel('Spacing by multiplication')\n",
    "\n",
    "\n",
    "\n",
    "# log scaling is in scientific notation by default;\n",
    "# here I change it to scalar format.\n",
    "from matplotlib.ticker import ScalarFormatter\n",
    "axs[1].yaxis.set_major_formatter(ScalarFormatter())\n",
    "\n",
    "# labels\n",
    "axs[0].set_title(r'$\\bf{A}$)  Linear scaling')\n",
    "axs[0].set_ylabel('Spacing by addition')\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_linVlog_line.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "tKDjqfjbb80C"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "QOLp__aN8ps0"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.24: Radial plots"
   ],
   "metadata": {
    "id": "Xj3VX-M9xw73"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# data (from https://www.timeanddate.com/weather/%403841798/climate)\n",
    "tempC = [ 26,25,23,19,15,11,11,13,16,19,22,25 ]\n",
    "months = [ 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' ]\n",
    "\n",
    "# angles for plotting\n",
    "theta = np.linspace(0,2*np.pi,len(months)+1)\n",
    "\n",
    "# repeat first data point so the line wraps around\n",
    "tempC.append(tempC[0])\n",
    "\n",
    "\n",
    "## draw the data\n",
    "ax = plt.subplot(111,polar=True)\n",
    "ax.plot(theta,tempC,'ko-')\n",
    "ax.fill(theta,tempC,'k',alpha=.1)\n",
    "\n",
    "# make the plot look nicer\n",
    "ax.set_xticks(theta[:-1])\n",
    "ax.set_xticklabels(months)\n",
    "ax.set_yticks([10,20,30])\n",
    "ax.set_ylim([0,30])\n",
    "ax.set_title('High temps ($^{\\circ}$C) near Patagonia',y=1.15,loc='center')\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_radialGood.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "nNg0DuRPxyM2"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "P7CjOyRTi0U7"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Figure 3.25: When not to use a radial plot"
   ],
   "metadata": {
    "id": "PUrytCEeiz-s"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# fake data\n",
    "data = {\n",
    "    'Horror' : 8 ,\n",
    "    'Romcom' : 1 ,\n",
    "    'Scifi'  : 9 ,\n",
    "    'Action' : 7 ,\n",
    "    'Anime'  : 3 ,\n",
    "    'Docu'   : 6\n",
    "}\n",
    "\n",
    "# angles for plotting\n",
    "theta = np.linspace(0,2*np.pi,len(data)+1)\n",
    "\n",
    "# repeat first data point so the line wraps around\n",
    "ratings = list(data.values())\n",
    "ratings.append(ratings[0])\n",
    "\n",
    "## draw the data\n",
    "ax = plt.subplot(111,polar=True)\n",
    "ax.plot(theta,ratings,'ko-')\n",
    "ax.fill(theta,ratings,'k',alpha=.1)\n",
    "\n",
    "# make the plot look nicer\n",
    "ax.set_xticks(theta[:-1])\n",
    "ax.set_xticklabels(data.keys())\n",
    "ax.set_yticks([5,10])\n",
    "ax.set_ylim([0,10])\n",
    "ax.set_title('Movie genre preferences',y=1.1,loc='center')\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_radialBad.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "hSMgq8HKxw5N"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "vNRjofj6-f7a"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 1"
   ],
   "metadata": {
    "id": "U_8TZTpI-f4o"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# grouped bar plots both ways using pandas\n",
    "df = pd.DataFrame( np.reshape(np.arange(12),(4,3)),\n",
    "                   columns=['A','B','C'] )\n",
    "df.T"
   ],
   "metadata": {
    "id": "o__bqFfb-iCE"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "fig,axs = plt.subplots(1,2,figsize=(12,5))\n",
    "\n",
    "df.plot(kind='bar',ax=axs[0],title='Grouping by columns',\n",
    "        xlabel='Category',ylabel='Value',rot=0)\n",
    "\n",
    "df.T.plot(kind='bar',ax=axs[1],title='Grouping by rows',\n",
    "        xlabel='Category',ylabel='Value',rot=0)\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "EdickYaU-iEO"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "geiodppx-iGN"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 2 (Figure 3.7)"
   ],
   "metadata": {
    "id": "vKwHiBaP-f1u"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "6IgOgUtIGFwR"
   },
   "outputs": [],
   "source": [
    "## create data for the bar plot\n",
    "\n",
    "# data sizes\n",
    "m = 30 # rows\n",
    "n =  6 # columns\n",
    "\n",
    "# generate data\n",
    "data = np.zeros((m,n))\n",
    "\n",
    "for i in range(n):\n",
    "  # You can implement the equation directly based on mean=0 and std=1\n",
    "  data[:,i] = 30*np.random.randn(m) * (2*i/(n-1)-1)**2 + (i+1)**2\n",
    "\n",
    "  # or you can specify mu and sigma as parameters\n",
    "  # data[:,i] = np.random.normal(loc=(i+1)**2, scale=30*(2*i/(n-1)-1)**2, size=m)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "GUwco_74GFwR"
   },
   "outputs": [],
   "source": [
    "# show the bars!\n",
    "\n",
    "fig,ax = plt.subplots(1,3,figsize=(10,3))\n",
    "\n",
    "# 'naked' bars\n",
    "ax[0].bar(range(n),np.mean(data,axis=0),color='k')\n",
    "ax[0].set_xticks(range(n))\n",
    "ax[0].set_title(r'$\\bf{A}$)  Bar plot')\n",
    "\n",
    "# just the error bars\n",
    "ax[1].errorbar(range(n),np.mean(data,axis=0),np.std(data,axis=0,ddof=1),\n",
    "               marker='.',linestyle='',color='k')\n",
    "ax[1].set_xticks(range(n))\n",
    "ax[1].set_title(r'$\\bf{B}$)  Error plot')\n",
    "\n",
    "# both\n",
    "ax[2].bar(range(n),np.mean(data,axis=0),color='gray')\n",
    "ax[2].set_xticks(range(n))\n",
    "ax[2].errorbar(range(n),np.mean(data,axis=0),np.std(data,axis=0,ddof=1),marker='.',linestyle='',color='k')\n",
    "ax[2].set_title(r'$\\bf{C}$)  Error bar plot')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_errorbar.png')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "oXP5VaoDrA6V"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 3"
   ],
   "metadata": {
    "id": "Q9rbQJf7rA80"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# A pie chart is appropriate here because the data\n",
    "# can be converted to proportion (although they are\n",
    "# not given as proportion, so transformation is needed).\n",
    "\n",
    "# data and labels, specified in a dictionary\n",
    "d = {\n",
    "    'Chocolate': 24,\n",
    "     'Vanilla' : 16,\n",
    "  'Strawberry' :  7,\n",
    "   'Pistachio' : 16\n",
    "   }\n",
    "\n",
    "\n",
    "# note that plt.pie() automatically computes proportion\n",
    "plt.pie(d.values(), labels=d.keys(),\n",
    "        explode=[0,.1,0,.15],autopct='%.1f%%',radius=1.2 )\n",
    "\n",
    "# you can also do it manually:\n",
    "# x = list( d.values() )\n",
    "# x = x/np.sum(x)\n",
    "# plt.pie(x, labels=d.keys() )\n",
    "\n",
    "plt.savefig('vis_ex_pie.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "d25370_zJAnj"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "j2I9dYPj-fzD"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 4"
   ],
   "metadata": {
    "id": "pTld_T4xd8Kl"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "data = np.random.gamma(2,2,size=500)\n",
    "\n",
    "# raw histogram values\n",
    "y,x = np.histogram(data,bins=40)\n",
    "x = (x[:-1]+x[1:])/2\n",
    "\n",
    "# normalize to percent\n",
    "yp = (100*y) / np.sum(y)\n",
    "\n",
    "_,axs = plt.subplots(1,2,figsize=(10,5))\n",
    "\n",
    "axs[0].plot(x,y,linewidth=3,color='k')\n",
    "axs[0].set_title(r'$\\bf{A}$)  Counts (sum=%g)' %np.sum(y))\n",
    "axs[0].set_ylabel('Counts')\n",
    "axs[0].set_xlabel('Data values')\n",
    "axs[0].set_xlim(x[[0,-1]])\n",
    "\n",
    "\n",
    "axs[1].plot(x,yp,linewidth=3,color='k')\n",
    "axs[1].set_title(r'$\\bf{B}$)  Percentage (sum=%g)' %np.sum(yp))\n",
    "axs[1].set_ylabel('Percentage')\n",
    "axs[1].set_xlabel('Data values')\n",
    "axs[1].set_xlim(x[[0,-1]])\n",
    "\n",
    "plt.savefig('vis_ex_histCountPerc.png')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "GhryzhQSp23k"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "2zaXypnhp26Q"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 5"
   ],
   "metadata": {
    "id": "4Cl8I3y0-fv7"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# parameters\n",
    "N = 200 # sample sizes\n",
    "k = 30 # number of bins\n",
    "\n",
    "# create the data\n",
    "d1 = np.random.randn(N) + 2\n",
    "d2 = np.exp( np.random.randn(N) )\n",
    "\n",
    "# define the bins\n",
    "alldata = np.hstack((d1,d2))\n",
    "bins = np.linspace(np.min(alldata),np.max(alldata),k+1)\n",
    "\n",
    "# get histogram values using numpy\n",
    "y1,x1 = np.histogram(d1,bins=bins)\n",
    "y2,x2 = np.histogram(d2,bins=bins)\n",
    "xx = (x1[:-1]+x1[1:])/2\n",
    "\n",
    "\n",
    "### now for plotting\n",
    "_,axs = plt.subplots(1,2,figsize=(10,3))\n",
    "\n",
    "# draw the histograms using matplotlib\n",
    "axs[0].hist(d1,bins=bins,label='Gaussian',alpha=.5,color=(0,0,0))\n",
    "axs[0].hist(d2,bins=bins,label='Exponential',alpha=.5,color=(.6,.6,.6))\n",
    "axs[0].set_title(r'$\\bf{A}$) Histograms using bars')\n",
    "\n",
    "# now draw lines\n",
    "axs[1].plot(xx,y1,linewidth=3,label='Gaussian',color=(0,0,0))\n",
    "axs[1].plot(xx,y2,linewidth=3,label='Exponential',color=(.6,.6,.6))\n",
    "axs[1].set_title(r'$\\bf{B}$) Histograms using lines')\n",
    "\n",
    "for a in axs:\n",
    "  a.set_xlabel('Data value')\n",
    "  a.set_ylabel('Count')\n",
    "  a.legend()\n",
    "\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_ex_histBarsLines.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "SSe4Mi7nR2n9"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "orEpPWq4R2q5"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 6"
   ],
   "metadata": {
    "id": "8gv2sNCGR2tw"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "x = np.linspace(0,6,1001)\n",
    "\n",
    "# plot same function in both axes\n",
    "_,axs = plt.subplots(1,2,figsize=(10,4))\n",
    "for i,a in enumerate(axs):\n",
    "  a.plot(x,x, linewidth=2, label='$y=x$', color=[0,0,0])\n",
    "  a.plot(x,np.exp(x), '--', linewidth=2, label='$y=e^x$', color=[.7,.7,.7])\n",
    "  a.legend()\n",
    "  a.set_title([r'$\\bf{A}$)',r'$\\bf{B}$)'][i] + '  ' + ['Linear','Logarithmic'][i] + ' y-axis scale')\n",
    "  a.set_xlim([np.min(x),np.max(x)])\n",
    "\n",
    "\n",
    "# axis-specific adjustments\n",
    "axs[0].set_ylim([0,100])\n",
    "axs[1].set_yscale('log')\n",
    "\n",
    "# save and display\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_ex_linlog.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "0EwbMEXsR2wo"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "### Comment about using -x instead of x:\n",
    "# The left plot (linear scale) shows the expected result.\n",
    "# The right plot (log scale) is missing the linear function.\n",
    "#   This happens because the log of a negative number does not exist in the real numbers.\n",
    "#   Therefore, logarithmic y-axis scaling is valid only for positive-valued data.\n",
    "#"
   ],
   "metadata": {
    "id": "KYEDltipR2zl"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "GQ1XrnpfR22X"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 7"
   ],
   "metadata": {
    "id": "ozLiJ-Y8R25Q"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# create data\n",
    "\n",
    "df = pd.DataFrame(np.hstack((np.random.randn(123,1),np.random.rand(123,1))),\n",
    "                  columns = ['norm','unif'])\n",
    "\n",
    "_,axs = plt.subplots(1,2,figsize=(9,5))\n",
    "sns.violinplot(data=df,palette='gray',ax=axs[0])\n",
    "sns.stripplot(data=df,ax=axs[0],palette='dark:w')\n",
    "axs[0].set(xlabel='Distribution type',ylabel='Data value',ylim=[-3.5,3.5])\n",
    "axs[0].set_title(r'$\\bf{A}$) Two symmetric violin plots')\n",
    "\n",
    "# need to combine the columns into one\n",
    "df_all = pd.DataFrame( pd.concat((df['norm'],df['unif']),axis=0),columns=['y'])\n",
    "# and create a separate column to indicate the distribution type\n",
    "df_all['distr'] = 'unif'\n",
    "df_all['distr'][:len(df)] = 'norm' # note: you can safely ignore the warning that this line produces\n",
    "df_all[''] = ' '\n",
    "\n",
    "# now we can create a split violin plot\n",
    "sns.violinplot(data=df_all,x='',y='y',palette='gray',\n",
    "               ax=axs[1],split=True,hue='distr')\n",
    "axs[1].set(xlim=[-.5,.5],ylabel='Data value',ylim=[-3.5,3.5])\n",
    "axs[1].set_title(r'$\\bf{B}$) One asymmetric violin plot')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('vis_ex7.png')\n",
    "plt.show()"
   ],
   "metadata": {
    "id": "Eqe5e-Fld8B9"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "u9sCCnuod8H_"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Exercise 8"
   ],
   "metadata": {
    "id": "b5fiTJ7-p287"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# data (from https://www.timeanddate.com/weather/%403841798/climate)\n",
    "tempC = [ 26,25,23,19,15,11,11,13,16,19,22,25 ]\n",
    "months = [ 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' ]\n",
    "\n",
    "# import to pandas\n",
    "df = pd.DataFrame(data=list(zip(tempC,months)),columns=['Temp','Month'])\n",
    "\n",
    "# import plotly\n",
    "import plotly.express as px\n",
    "\n",
    "# plotly! (this produces a basic radial plot; you can explore line_polar() for more customizations)\n",
    "fig = px.line_polar(df,r='Temp',theta='Month',line_close=True)\n",
    "fig.show()\n",
    "\n",
    "# Note: If you get a warning message about pandas.concat, this is coming from inside the line_polar function.\n",
    "#       I'll update the code if this ends up breaking. For now, ignore the warning."
   ],
   "metadata": {
    "id": "-oPChOhEp2_i"
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [],
   "metadata": {
    "id": "OerQoNt7DJee"
   },
   "execution_count": null,
   "outputs": []
  }
 ]
}